import java.nio.channels.*;
import java.nio.channels.spi.SelectorProvider;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.*;
import java.net.*;
import java.util.*;
import java.io.IOException;

/**
 * @author Team 29
 * Sends and recieves messages for the client through the network.
 * Establishes a connection to the server.
 *
 */
public class ClientSender {

	Selector select;
	SocketChannel sc;
	ByteBuffer inbuff= ByteBuffer.allocateDirect(1024);
	CharsetDecoder decoder = Charset.forName("ISO-8859-1").newDecoder();
	String serverip;
	
	public ClientSender(String ip){
		serverip = ip;
		try{
			select = SelectorProvider.provider().openSelector();
		}
		catch(IOException e)
		{System.out.println("Selector provider error"); System.exit(-1);}	
		
		connect();
		try{sc.register(select, SelectionKey.OP_READ);}
		catch(Exception e)
		{System.out.println("closed channel exception"); System.exit(-1);}
	}//end constructor
	
	/**
	 * connects to server
	 */
	private void connect()
	{
		try{
			sc = SocketChannel.open();
			sc.configureBlocking(true);
			sc.connect(new InetSocketAddress(InetAddress.getByName(serverip), 50000));
			sc.configureBlocking(false);	
			}
		catch(IOException e)
		{System.out.println("Error establishing connection"); System.exit(-1);}
	}//end connect
	
	public void send(String s)
	{
		try{sc.register(select, SelectionKey.OP_WRITE);}
		catch(Exception e)
		{System.out.println("closed channel exception"); System.exit(-1);}
		ByteBuffer outbuff = ByteBuffer.allocateDirect(1024);
		outbuff.clear();
		outbuff.put(Cryptography.encryptToBytes(s));
		outbuff.flip();
		try{
			while(outbuff.hasRemaining()){
				sc.write(outbuff);
			}
		}catch(IOException e){System.out.println("sending problem");}
		
		try{sc.register(select, SelectionKey.OP_READ);}
		catch(Exception e)
		{System.out.println("closed channel exception"); System.exit(-1);}
	}//end send
	
	/**
	 * @return
	 * reads any inbound traffic from server
	 */
	public String read()
	{
		String s = null;	
		try {select.select();} 
		catch (IOException e) 
		{e.printStackTrace();}
		Iterator iter = select.selectedKeys().iterator();
		SelectionKey key;
		while(iter.hasNext()){
			key = (SelectionKey) iter.next();
			iter.remove(); 
			if(key.isReadable()){
			s= readFromChannel();
			}
			else {System.out.println("CLIENT recieved unreadable msg");
			System.exit(-1);}
		}
		return s;
	}//end read
	
	private String readFromChannel(){
		String msg = null;
		try{
			int num = sc.read(inbuff);
			if(num ==-1) return null;
			else
				{msg = getMessageFromBuffer(inbuff);}
		}
		catch(IOException i){
			System.out.println("CLIENT error reading msg");
			System.exit(-1);
			}
		return msg;
	}//end readfromchannel
	
	private String getMessageFromBuffer(ByteBuffer buff){
		String msg = null;
		int pos = buff.position();
		int limit = buff.limit();
		buff.position(0);
		buff.limit(pos);
		try{			
			CharBuffer cbuff = decoder.decode(buff);
			System.out.println("Raw message recieved: " + cbuff.toString());
			msg = Cryptography.decrypt(cbuff.toString());
		}catch(Exception e){System.out.println("some decoding error");}
		buff.clear();
		return msg;
	}//getmessagefrombuffer
}//end class
